package org.example.spring;

import org.example.spring.Bean.BeanDefinition;
import org.example.spring.annotions.Autowired;
import org.example.spring.annotions.Component;
import org.example.spring.annotions.ComponentScan;
import org.example.spring.annotions.Scope;
import org.example.spring.constans.ScopeType;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 应用程序上下文
 *
 * @author changxinglong
 * @version 1.0.0
 * @since 2024/10/29
 */
public class ApplicationContext {
    /**
     * 存储用户的配置类
     */
    private Class configClass;
    /**
     * 用作缓存beanDefinition
     */
    private ConcurrentHashMap<String,BeanDefinition> beanDefinitionsMap = new ConcurrentHashMap<>();
    private ConcurrentHashMap<String,Object> singleObjects = new ConcurrentHashMap<>();

    /**
     * 应用程序上下文
     *
     * @param configClass 用户的应用配置类
     */
    public ApplicationContext(Class configClass) {

        this.configClass = configClass;
        //扫描
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            //componentScan注解上的路径
            String value = componentScanAnnotation.value();
            if("".equals(value)){throw new NullPointerException("未指定扫描路径");}
            String path = value.replace(".", "/");//org.example.service

            ClassLoader classLoader = ApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);

            File file = new File(resource.getFile());
            if (file.isDirectory()) {
                File[] files = file.listFiles();
                for (File f : files) {
                    try {
                        String classPath = path + "/" + f.getName();
                        if (classPath.endsWith(".class")) {
                            classPath = classPath.replace(".class", "").replace("/", ".");
                            Class<?> clazz = classLoader.loadClass(classPath);
                            if (clazz.isAnnotationPresent(Component.class)) {
                                String beanName = clazz.getAnnotation(Component.class).value();//获取Bean名称
                                if("".equals(beanName)){
                                   beanName =  Introspector.decapitalize(clazz.getSimpleName());
                                }
                                //BeanDefinition
                                BeanDefinition beanDefinition = new BeanDefinition();
                                beanDefinition.setType(clazz);
                                if (clazz.isAnnotationPresent(Scope.class)) {
                                    beanDefinition.setScope(clazz.getAnnotation(Scope.class).value());

                                } else {
                                    beanDefinition.setScope(ScopeType.SINGLETON);
                                }
                                beanDefinitionsMap.put(beanName,beanDefinition);    //存入beanDefinition
                            }
                        }
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }


        //实例化单例Bean
        for (String beanName : beanDefinitionsMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionsMap.get(beanName);
            if (ScopeType.SINGLETON.equals(beanDefinition.getScope())){ //      //如果是单例则直接进行初始化
                Object bean = createBean(beanName,beanDefinition);
                singleObjects.put(beanName,bean);
            }
        }
    }
    private Object createBean(String beanName,BeanDefinition beanDefinition){
        Class clazz = beanDefinition.getType();
//       必须有无参构造方法
        try {
            Object instance = clazz.getConstructor().newInstance();
            //简单版依赖注入的实现
            for (Field f : clazz.getDeclaredFields()) {
                f.setAccessible(true);
                if(f.isAnnotationPresent(Autowired.class)){
                    //把属性名字当做bean的名字
                    f.set(instance,getBean(f.getName()));
                }
            }

            return instance;
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取 Bean
     *
     * @param beanName Bean 名称
     * @return {@code Object}
     */
    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionsMap.get(beanName);
        if(beanDefinition==null){
            throw new NullPointerException("beanDefinition为空，未获取到Bean信息，无法获取该bean");
        }else if ((ScopeType.SINGLETON).equals(beanDefinition.getScope())){
            //单例,如果 beanName 在 singleObjects 中不存在，则会创建新的 bean，并将其存入 singleObjects 中，存在则直接返回该bean。
            return singleObjects.computeIfAbsent(beanName, aBean -> createBean(aBean, beanDefinition));
        }else {
            //多例
           return createBean(beanName,beanDefinition);
        }
    }
}
